/*
 * UserNet  --  TCP/IP implementation in userspace
 * Copyright (C) 2010  Alex Diban
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "unet/arp.h"


struct list_head list_arp = {.next = &list_arp,.prev = &list_arp };
static uint8_t arp_init = 0;


int arp_count = 0;

struct unet_arp_entry *
list_head_to_arp_entry (struct list_head *l)
{
  return list_entry (l, struct unet_arp_entry, element);
}

int
unet_new_arp_entry (struct unet_arp_entry *arp_entry)
{
  pthread_mutex_lock (&list_arp_mutex);
  printf("(ARP): Request to add:\n");
  printf("(ARP): %s\n", inet_ntoa (arp_entry->ip));

  list_add (&arp_entry->element, &list_arp);
  arp_count++;
  pthread_mutex_unlock (&list_arp_mutex);
  return 0;
  
}

int
unet_count_arp ()
{
  return arp_count;
}

struct unet_arp_entry *
unet_get_arp (uint32_t id)
{

  int i = 0;
  struct list_head *iterador;
  list_for_each (iterador, &list_arp)
  {

    if (i == id)
      return list_head_to_arp_entry (iterador);
  }
  return NULL;
}

struct unet_arp_entry *
unet_mac_find (struct in_addr addr)
{

  struct list_head *iterador;
  list_for_each (iterador, &list_arp)
  {
    if (list_head_to_arp_entry (iterador)->ip.s_addr == addr.s_addr)

      return list_head_to_arp_entry (iterador);
  }
  return NULL;
}

void
unet_arp_incoming_packet (struct unet_device *ud, const uint8_t * packet,
			  const uint32_t len)
{

  if (arp_init == 0){
    pthread_mutex_init(&list_arp_mutex, 0);
    arp_init=1;
  }
  struct unet_arphdr *arp_packet;
  arp_packet = (struct unet_arphdr *) packet;

  printf("(ARP): ARP: Incoming ARP packet with size %d\n",len);
  printf("(ARP): ARP: format of hardware address:%d\n", ntohs(arp_packet->ar_hrd));
  printf("(ARP): ARP: format of protocol address:%02x\n", ntohs(arp_packet->ar_pro));
  printf("(ARP): ARP: length of hardware address:%X\n", arp_packet->ar_hln);
  printf("(ARP): ARP: length of protocol address:%X\n", arp_packet->ar_pln);
  printf("(ARP): ARP: ARP opcode (command):%d\n", ntohs(arp_packet->ar_op));

  printf("(ARP): ARP: Sender MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
	  arp_packet->ar_sha[0], arp_packet->ar_sha[1], arp_packet->ar_sha[2],
	  arp_packet->ar_sha[3], arp_packet->ar_sha[4],
	  arp_packet->ar_sha[5]);

  printf("(ARP): ARP: Sender IP address: %d.%d.%d.%d\n", arp_packet->ar_sip[0],
	  arp_packet->ar_sip[1], arp_packet->ar_sip[2],
	  arp_packet->ar_sip[3]);

  printf("(ARP): ARP: Target MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
	  arp_packet->ar_tha[0], arp_packet->ar_tha[1], arp_packet->ar_tha[2],
	  arp_packet->ar_tha[3], arp_packet->ar_tha[4],
	  arp_packet->ar_tha[5]);

  printf("(ARP): ARP: Target IP address: %d.%d.%d.%d\n", arp_packet->ar_tip[0],
	  arp_packet->ar_tip[1], arp_packet->ar_tip[2],
	  arp_packet->ar_tip[3]);

  if (ntohs(arp_packet->ar_op) == ARPOP_REQUEST)
    {

      //Miramos si la MAC pedida es la de nuestra interficie:  
      struct in_addr addr;
      char IP[10];
      sprintf (IP, "%d.%d.%d.%d", arp_packet->ar_tip[0],
	       arp_packet->ar_tip[1], arp_packet->ar_tip[2],
	       arp_packet->ar_tip[3]);
      addr.s_addr = inet_addr (IP);
      printf("(ARP): ARP: IP del dispositivo=%s\n", inet_ntoa (ud->ip_addr));

      if (addr.s_addr == ud->ip_addr.s_addr)
	{

	  printf("(ARP): ARP: la IP que nos piden es la nuestra\n");
	  //Enviamos ARP REPLY
	  struct unet_arphdr *arp_reply_packet;
	  arp_reply_packet = malloc (sizeof (struct unet_arphdr));

	  arp_reply_packet->ar_hrd = arp_packet->ar_hrd;
	  arp_reply_packet->ar_pro = arp_packet->ar_pro;
	  arp_reply_packet->ar_hln = arp_packet->ar_hln;
	  arp_reply_packet->ar_pln = arp_packet->ar_pln;
	  arp_reply_packet->ar_op = htons(ARPOP_REPLY);

	  arp_reply_packet->ar_sha[0] = ud->mac_addr[0];
	  arp_reply_packet->ar_sha[1] = ud->mac_addr[1];
	  arp_reply_packet->ar_sha[2] = ud->mac_addr[2];
	  arp_reply_packet->ar_sha[3] = ud->mac_addr[3];
	  arp_reply_packet->ar_sha[4] = ud->mac_addr[4];
	  arp_reply_packet->ar_sha[5] = ud->mac_addr[5];

	  arp_reply_packet->ar_sip[0] = arp_packet->ar_tip[0];
	  arp_reply_packet->ar_sip[1] = arp_packet->ar_tip[1];
	  arp_reply_packet->ar_sip[2] = arp_packet->ar_tip[2];
	  arp_reply_packet->ar_sip[3] = arp_packet->ar_tip[3];

	  arp_reply_packet->ar_tha[0] = arp_packet->ar_sha[0];
	  arp_reply_packet->ar_tha[1] = arp_packet->ar_sha[1];
	  arp_reply_packet->ar_tha[2] = arp_packet->ar_sha[2];
	  arp_reply_packet->ar_tha[3] = arp_packet->ar_sha[3];
	  arp_reply_packet->ar_tha[4] = arp_packet->ar_sha[4];
	  arp_reply_packet->ar_tha[5] = arp_packet->ar_sha[5];

	  arp_reply_packet->ar_tip[0] = arp_packet->ar_sip[0];
	  arp_reply_packet->ar_tip[1] = arp_packet->ar_sip[1];
	  arp_reply_packet->ar_tip[2] = arp_packet->ar_sip[2];
	  arp_reply_packet->ar_tip[3] = arp_packet->ar_sip[3];

	  printf("(ARP): \nARP: ARP REPLY:\n");
	  printf("(ARP): ARP: format of hardware address:%d\n",
		  ntohs(arp_reply_packet->ar_hrd));
	  printf("(ARP): ARP: format of protocol address:%02x\n",
		  ntohs(arp_reply_packet->ar_pro));
	  printf("(ARP): ARP: length of hardware address:%X\n",
		  arp_reply_packet->ar_hln);
	  printf("(ARP): ARP: length of protocol address:%X\n",
		  arp_reply_packet->ar_pln);
	  printf("(ARP): ARP: ARP opcode (command):%d\n", ntohs(arp_reply_packet->ar_op));
	  printf("(ARP): ARP: Sender MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
		  arp_reply_packet->ar_sha[0], arp_reply_packet->ar_sha[1],
		  arp_reply_packet->ar_sha[2], arp_reply_packet->ar_sha[3],
		  arp_reply_packet->ar_sha[4], arp_reply_packet->ar_sha[5]);

	  printf("(ARP): ARP: Sender IP address: %d.%d.%d.%d\n",
		  arp_reply_packet->ar_sip[0], arp_reply_packet->ar_sip[1],
		  arp_reply_packet->ar_sip[2], arp_reply_packet->ar_sip[3]);

	  printf("(ARP): ARP: Target MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
		  arp_reply_packet->ar_tha[0], arp_reply_packet->ar_tha[1],
		  arp_reply_packet->ar_tha[2], arp_reply_packet->ar_tha[3],
		  arp_reply_packet->ar_tha[4], arp_reply_packet->ar_tha[5]);

	  printf("(ARP): ARP: Target IP address: %d.%d.%d.%d\n",
		  arp_reply_packet->ar_tip[0], arp_reply_packet->ar_tip[1],
		  arp_reply_packet->ar_tip[2], arp_reply_packet->ar_tip[3]);

	  printf("(ARP): ARP: Seding ARP reply packet to ethernet\n");
	  
	  ud->driver.datalink.send_packet (ETHERTYPE_ARP,
					   arp_reply_packet->ar_tha, ud,
					   (const uint8_t *) arp_reply_packet,
					   ARP_PACKET_SIZE);
	  free(arp_reply_packet);
	  
	}
    }
  else if (ntohs(arp_packet->ar_op) == ARPOP_REPLY)
    {

      struct unet_arp_entry *arp_entry;
      arp_entry = malloc (sizeof (struct unet_arp_entry));
      
      printf("nos ha llegado una MAC JAJJA\n");

      arp_entry->mac_addr[0] = arp_packet->ar_sha[0];
      arp_entry->mac_addr[1] = arp_packet->ar_sha[1];
      arp_entry->mac_addr[2] = arp_packet->ar_sha[2];
      arp_entry->mac_addr[3] = arp_packet->ar_sha[3];
      arp_entry->mac_addr[4] = arp_packet->ar_sha[4];
      arp_entry->mac_addr[5] = arp_packet->ar_sha[5];

      char IP[10];
      sprintf (IP, "%d.%d.%d.%d", arp_packet->ar_sip[0],
	       arp_packet->ar_sip[1], arp_packet->ar_sip[2],
	       arp_packet->ar_sip[3]);
      printf ("La ip es :%d.%d.%d.%d\n", arp_packet->ar_sip[0],
	       arp_packet->ar_sip[1], arp_packet->ar_sip[2],
	       arp_packet->ar_sip[3]);
      arp_entry->ip.s_addr = inet_addr (IP);

      unet_new_arp_entry(arp_entry);
      
    }
}

int
unet_arp_get_mac (struct in_addr addr, uint8_t * mac, struct unet_device *ud)
{
  
  if (arp_init == 0){
    pthread_mutex_init(&list_arp_mutex, 0);
    arp_init=1;
  }
  printf("(ARP): ARP: looking for for %s MAC\n", inet_ntoa (addr));
  struct unet_arp_entry *arpentry = unet_mac_find (addr);
  if (arpentry == NULL)
    {
      printf("(ARP): ARP: %s not in ARP table, SENDING ARP REQUEST needed\n", inet_ntoa (addr));

      //Creamos un arp request packet
      struct unet_arphdr *arp_request_packet;
      arp_request_packet = malloc (sizeof (struct unet_arphdr));

      arp_request_packet->ar_hrd = htons(ARPHRD_ETHER);
      arp_request_packet->ar_pro = htons(0x0800);
      arp_request_packet->ar_hln = 6;
      arp_request_packet->ar_pln = 4;
      arp_request_packet->ar_op = htons(ARPOP_REQUEST);

      arp_request_packet->ar_sha[0] = ud->mac_addr[0];
      arp_request_packet->ar_sha[1] = ud->mac_addr[1];
      arp_request_packet->ar_sha[2] = ud->mac_addr[2];
      arp_request_packet->ar_sha[3] = ud->mac_addr[3];
      arp_request_packet->ar_sha[4] = ud->mac_addr[4];
      arp_request_packet->ar_sha[5] = ud->mac_addr[5];

      int i = 0;
      char *ptr;
      char *ipstring = (char *) inet_ntoa (ud->ip_addr);
      ptr = strtok (ipstring, ".");
      while (ptr != NULL)
	{
	  arp_request_packet->ar_sip[i] = atoi(ptr);
	  ptr = strtok (NULL, ".");
	  i++;
	}     
      
      arp_request_packet->ar_tha[0] = 0;
      arp_request_packet->ar_tha[1] = 0;
      arp_request_packet->ar_tha[2] = 0;
      arp_request_packet->ar_tha[3] = 0;
      arp_request_packet->ar_tha[4] = 0;
      arp_request_packet->ar_tha[5] = 0;

      i = 0;
      ptr = NULL;
      ipstring = (char *) inet_ntoa (addr);
      ptr = strtok (ipstring, ".");

      while (ptr != NULL)
	{
	  arp_request_packet->ar_tip[i] = atoi(ptr);
	  ptr = strtok (NULL, ".");
	  i++;
	}

      unsigned char bcast[ETH_ALEN];
      bcast[0] = 0xFF;
      bcast[1] = 0xFF;
      bcast[2] = 0xFF;
      bcast[3] = 0xFF;
      bcast[4] = 0xFF;
      bcast[5] = 0xFF;

      printf("(ARP): \nARP: ARP REQUEST:\n");
      printf("(ARP): ARP: format of hardware address:%d\n", ntohs(arp_request_packet->ar_hrd));
      printf("(ARP): ARP: format of protocol address:%02x\n", arp_request_packet->ar_pro);
      printf("(ARP): ARP: length of hardware address:%X\n", arp_request_packet->ar_hln);
      printf("(ARP): ARP: length of protocol address:%X\n", arp_request_packet->ar_pln);
      printf("(ARP): ARP: ARP opcode (command):%d\n", ntohs(arp_request_packet->ar_op));
      printf("(ARP): ARP: Sender MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
	      arp_request_packet->ar_sha[0], arp_request_packet->ar_sha[1],
	      arp_request_packet->ar_sha[2], arp_request_packet->ar_sha[3],
	      arp_request_packet->ar_sha[4], arp_request_packet->ar_sha[5]);

      printf("(ARP): ARP: Sender IP address: %d.%d.%d.%d\n",
	      arp_request_packet->ar_sip[0], arp_request_packet->ar_sip[1],
	      arp_request_packet->ar_sip[2], arp_request_packet->ar_sip[3]);

      printf("(ARP): ARP: Target MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
	      arp_request_packet->ar_tha[0], arp_request_packet->ar_tha[1],
	      arp_request_packet->ar_tha[2], arp_request_packet->ar_tha[3],
	      arp_request_packet->ar_tha[4], arp_request_packet->ar_tha[5]);

      printf("(ARP): ARP: Target IP address: %d.%d.%d.%d\n",
	      arp_request_packet->ar_tip[0], arp_request_packet->ar_tip[1],
	      arp_request_packet->ar_tip[2], arp_request_packet->ar_tip[3]);
	      
      printf("ARP: Sending ARP REQUEST through device with IP=%s\n",inet_ntoa(ud->ip_addr));
      ud->driver.datalink.send_packet (ETHERTYPE_ARP, bcast, ud,
				       (const uint8_t *) arp_request_packet,
				       ARP_PACKET_SIZE);

      free(arp_request_packet);
      
      while (arpentry == NULL){
	arpentry = unet_mac_find(addr);
      }
    }
  
    memcpy(mac,arpentry->mac_addr,sizeof(uint8_t)*6);

    printf("(ARP): ARP: %s MAC IS:%02x:%02x:%02x:%02x:%02x:%02x", inet_ntoa (addr),
    mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return 0;
}
